NX 有提供擴充工具幫助快速生成專案或是元件庫的架構,像是:
pnpm exec nx generate @nx/next:application
利用擴充工具 @nx/next
可以幫助快速建立一個跟 NX 整合好的 Next.js 專案,可是生成的預設模板是沒有 tailwind 設定的,如果想用 tailwind 的話還要額外做設定,做個一兩次還好,但每次添加專案都要重複的話就有點煩了。
這時候就要想辦法自訂義生成工具了,像 @nx/next:application
也是社群定義好的擴充工具,我們也可以做自己的工具。
首先安裝 NX 的擴充開發工具。
pnpm add -D @nx/plugin@latest
有了開發工具就能用指令生成插件模組,一樣會放在 lib 底下。
pnpm exec nx generate @nx/plugin:plugin --name plugins/next-plugin
現在新開的插件底下還什麼都沒有,來建立一個新的生成工具。
pnpm exec nx generate @nx/plugin:generator --name=application --plugin=plugins-next-plugin
生成一個叫 application
的生成工具。
來看一下有了增加了內容後的插件模組結構。
next-plugin
├── README.md
├── generators.json
├── jest.config.ts
├── package.json
├── project.json
├── src
│ ├── generators
│ │ └── application
│ │ ├── files
│ │ │ └── src
│ │ │ └── index.ts.template
│ │ ├── generator.spec.ts
│ │ ├── generator.ts
│ │ ├── schema.d.ts
│ │ └── schema.json
│ └── index.ts
├── tsconfig.json
├── tsconfig.lib.json
└── tsconfig.spec.json
generators.json
是 NX 用於識別每個插件有哪些指令可以用的進入點。
{
"generators": {
"application": {
"factory": "./src/generators/application/generator",
"schema": "./src/generators/application/schema.json",
"description": "application generator"
}
}
}
factory
指向實際指令執行的程式碼, schema
指向定義指令有哪先參數的設定檔。
這邊為了節省篇幅,先直接提供修改後的 generator 程式碼,再回頭說明。
// libs/plugins/next-plugin/src/generators/application/generator.ts
import { Tree } from '@nx/devkit';
import { applicationGenerator as nextApplicationGenerator } from '@nx/next';
import { setupTailwindGenerator } from '@nx/react';
type ApplicationGeneratorSchema = Parameters<
typeof nextApplicationGenerator
>[1];
type SetupTailwindOptions = Parameters<typeof setupTailwindGenerator>[1];
export async function applicationGenerator(
tree: Tree,
options: ApplicationGeneratorSchema,
) {
await nextApplicationGenerator(tree, {
...options,
style: 'none',
});
const tailwindOptions: SetupTailwindOptions = {
project: options.name,
};
await setupTailwindGenerator(tree, tailwindOptions);
}
export default applicationGenerator;
首先生成器是可以組合的,這邊主要組合了 @nx/next:application
跟 @nx/react:setup-tailwind
兩個步驟,分別對應 nextApplicationGenerator
跟 setupTailwindGenerator
這兩個函式。
而原本 @nx/next:application
有 style
參數可以指定專案要用什麼樣的樣式生成器,但因為我要用 tailwind ,就直接覆寫為 none
。
再來 schema.json
要定義指令能使用的參數,這邊就全部照抄 @nx/next:application
的定義,只是要改一下 $id
跟移除 style
參數。
{
"$schema": "http://json-schema.org/schema",
"cli": "nx",
"$id": "NextApp",
"title": "Create a Next.js Application for Nx",
"description": "Create a Next.js Application for Nx.",
"type": "object",
"properties": {
"name": {
"description": "The name of the application.",
"type": "string",
"$default": {
"$source": "argv",
"index": 0
},
"x-prompt": "What name would you like to use for the application?",
"pattern": "^[a-zA-Z].*$",
"x-priority": "important"
},
"directory": {
"description": "The directory of the new application.",
"type": "string",
"alias": "d",
"x-priority": "important"
},
"linter": {
"description": "The tool to use for running lint checks.",
"type": "string",
"enum": ["eslint"],
"default": "eslint"
},
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",
"default": false,
"x-priority": "internal"
},
"unitTestRunner": {
"type": "string",
"enum": ["jest", "none"],
"description": "Test runner to use for unit tests.",
"default": "jest"
},
"e2eTestRunner": {
"type": "string",
"enum": ["cypress", "playwright", "none"],
"description": "Test runner to use for end to end (E2E) tests.",
"x-prompt": "Which E2E test runner would you like to use?",
"default": "cypress"
},
"tags": {
"type": "string",
"description": "Add tags to the application (used for linting).",
"alias": "t"
},
"js": {
"type": "boolean",
"description": "Generate JavaScript files rather than TypeScript files.",
"default": false
},
"setParserOptionsProject": {
"type": "boolean",
"description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.",
"default": false
},
"swc": {
"description": "Enable the Rust-based compiler SWC to compile JS/TS files.",
"type": "boolean",
"default": true
},
"customServer": {
"description": "Use a custom Express server for the Next.js application.",
"type": "boolean",
"default": false
},
"skipPackageJson": {
"type": "boolean",
"default": false,
"description": "Do not add dependencies to `package.json`.",
"x-priority": "internal"
},
"appDir": {
"type": "boolean",
"default": true,
"description": "Enable the App Router for this project.",
"x-prompt": "Would you like to use the App Router (recommended)?"
},
"rootProject": {
"description": "Create an application at the root of the workspace.",
"type": "boolean",
"default": false,
"hidden": true,
"x-priority": "internal"
}
},
"required": []
}
這樣就能解省掉建立專案後添加 Tailwind 設定的步驟了,還有一些細部的修改可以陸續追加。
測試指令:
pnpm exec nx generate @ironman-nextjs/plugins/next-plugin:application